package dico.finder;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import dico.finder.impl.SimpleWord;
import dico.finder.tool.MultiMap;
import dico.finder.tool.StringOperation;

/**
 * class to explore the dictionary file to find all words
 * 
 * @author thomas
 *
 */
public class DicoAnalyser {
	/**
	 * list of all words in the dictionnary
	 */
	private List<Word> words_list;
	
	/**
	 * the list of all word by anagrame
	 */
	private MultiMap<String,Word> word_by_anagrame = null;
	
	/**
	 * the word factory
	 */
	private WordFactory word_factory;
	
	/**
	 * create the dico_analyse with a filereader
	 * 
	 * @param word_factory the word factory
	 * @param reader the reader
	 * 
	 * @throws IOException exctption when the reader have a bug
	 */
	public DicoAnalyser(final WordFactory word_factory, final FileReader reader) throws IOException {
		this.word_factory = word_factory;
		this.words_list = new ArrayList<Word>();
		
		final BufferedReader buf_reader = new BufferedReader(reader);
		
		String current_line = null;
		/** read each line of the dictionnary */
		while((current_line = buf_reader.readLine()) != null) {
			final Word current_word = this.word_factory.create(current_line);
			this.words_list.add(current_word);
		}
		
		buf_reader.close();
	}
	
	/**
	 * 
	 * @param word_factory the word factory
	 * @param dico_file the who represent the dictionnary
	 * @throws IOException
	 */
	public DicoAnalyser(final WordFactory word_factory,final File dico_file) throws IOException {
		this(word_factory, new FileReader(dico_file));
	}
	
	/**
	 * get all the word in the dictionnary
	 * 
	 * @return all the word find in the dictionnary
	 */
	public List<Word> getAllWord()
	{
		return this.words_list;
	}
	
	/**
	 * return true if the word is contain in the dictionary
	 * 
	 * @param word the search word
	 * 
	 * @return true if it's contains
	 */
	public boolean contains(String word)
	{
		return Collections.binarySearch(this.words_list, 
										new SimpleWord(word.trim(), null),
										new Comparator<Word>() {

											@Override
											public int compare(Word o1, Word o2) {
												return o1.getFinderWord().compareTo(o2.getFinderWord());
											}
										}) >= 0;
	}
	
	/**
	 * get all word who are an anagrame of this word
	 * 
	 * @param anagrame the anagrame
	 * 
	 * @return all the word
	 */
	public List<Word> getAllWord(String anagrame) {
		/** create a map for a better performance */
		if (this.word_by_anagrame == null) {
			this.word_by_anagrame = new MultiMap<String,Word>();
			for (Word word : this.words_list)
				this.word_by_anagrame.putValue(word.getAlphabeticalOrder(), word);
		}
		
		char[] chars = anagrame.toCharArray();
		Arrays.sort(chars);
		String ordered_chars = (new String(chars)).toUpperCase();
		return this.word_by_anagrame.get(ordered_chars);
	}
	
	/**
	 * get all word who begin by this word
	 * 
	 * @param word the search word
	 * 
	 * @return the list of word who begin by this word
	 */
	public List<Word> getWordBeginBy(String word)
	{
		return DicoAnalyser.getWordBeginBy(this.words_list, word);
	}
	
	/**
	 * get all word who begin by this word
	 * 
	 * @param words the list of word to search
	 * @param word the search word
	 * 
	 * @return the list of word who begin by this word
	 */
	public static List<Word> getWordBeginBy(List<Word> words, String word)
	{
		word = StringOperation.transform(word, StringOperation.LOWER_CASE|StringOperation.WITHOUT_ACCENTS);
		
		List<Word> new_words = new LinkedList<Word>();
		
		boolean already_add = false;
		for (Word current_word : words) {
			// if the word begin , so alors we add it in the list
			if (current_word.getFinderWord().indexOf(word) == 0) {
				new_words.add(current_word);
				already_add = true;
			} else if (already_add) {
				// if we have already add a word we can break the "for" therfore the liste is in alphabetical order
				break;
			}
		}
		
		return new_words;
	}
}
